﻿using System;
using System.Linq.Dynamic.Core;
using System.Linq;
using System.Threading.Tasks;

using Dapper;

using Jack.EntityFrameworkCore.WebAPITest.Entities;

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using Jack.EntityFrameworkCore.WebAPITest.EventListeners;
using Jack.EntityFrameworkCore.WebAPITest.Dapper.Logging;
using Microsoft.EntityFrameworkCore;

namespace Jack.EntityFrameworkCore.WebAPITest.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class UserModelController : ControllerBase
    {
        //public UserModelController(IRepository<UserModel> userRepository, IUnitOfWork unitOfWork)
        //{
        //    UserRepository = userRepository;
        //    UnitOfWork = unitOfWork;
        //}

        public IRepository<UserModel> UserRepository { get; set; }
        public IUnitOfWork UnitOfWork { get; set; }

        [HttpPost]
        public async Task<ActionResult<UserModel>> RandomAddUserForRepository()
        {
            UserModel userModel = new UserModel
            {
                UserName = Guid.NewGuid().ToString(),
                Age = new Random(Environment.TickCount).Next(1, 120),
            };
            await UserRepository.Insert(userModel);
            await UnitOfWork.SaveChanges();

            return userModel;
        }

        [HttpPost]
        public async Task<ActionResult<UserModel>> RandomAddUserForDapper()
        {
            UserModel userModel = new UserModel
            {
                UserName = Guid.NewGuid().ToString(),
                Age = new Random(Environment.TickCount).Next(1, 120),
            };

            UnitOfWork.Open();
            userModel.Id = await UnitOfWork.ExecuteScalarAsync<int>("INSERT INTO `user` (UserName, Age) VALUES (@UserName, @Age); SELECT LAST_INSERT_ID();", userModel);
            UnitOfWork.Close();

            return userModel;
        }

        [HttpGet]
        public async Task<ActionResult<IPagedList<UserModel>>> GetPagedListForRepository([Required, Range(1, 1000)] int page = 1, [Required, Range(1, 100)] int pageSize = 10)
        {
            var pagedList = await UserRepository.Where(x => x.Id > 0).OrderByDescending(x => x.Age).ToPagedListAsync(page, pageSize);
            return Ok(pagedList);
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<UserModel>>> GetPagedListForDapper([Required, Range(1, 1000)] int page = 1, [Required, Range(1, 100)] int pageSize = 10)
        {
            int skip = (page - 1) * pageSize;
            int take = pageSize;
            //using SqlClientListener sqlClientListener = new SqlClientListener();
            UnitOfWork.Open();
            var pagedList = await UnitOfWork.QueryAsync<UserModel>("SELECT * FROM `user` WHERE Id > 0 ORDER BY Age DESC LIMIT @skip, @take", new { skip, take });
            UnitOfWork.Close();

            return Ok(pagedList);
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<UserModel>>> GetPagedListForDapperLogging([FromServices] IDbConnectionFactory<Entities.DemoDbContext> dbConnectionFactory, [FromServices] Entities.DemoDbContext demoDbContext, [Required, Range(1, 1000)] int page = 1, [Required, Range(1, 100)] int pageSize = 10)
        {
            int skip = (page - 1) * pageSize;
            int take = pageSize;
            //using SqlClientListener sqlClientListener = new SqlClientListener();

            using var dbConn = dbConnectionFactory.CreateConnection(demoDbContext);
            dbConn.Open();
            var pagedList = await dbConn.QueryAsync<UserModel>("SELECT * FROM `user` WHERE Id > 0 ORDER BY Age DESC LIMIT @skip, @take", new { skip, take });
            dbConn.Close();

            return Ok(pagedList);
        }

        [HttpGet]
        public async Task UpdateSample([FromServices] IDbConnectionFactory<Entities.DemoDbContext> dbConnectionFactory, [FromServices] Entities.DemoDbContext demoDbContext)
        {
            var user = await UserRepository.FirstOrDefaultAsync(x => x.Id == 1);
            user.UserName = Guid.NewGuid().ToString();

            using var dbConn = dbConnectionFactory.CreateConnection(demoDbContext);
            dbConn.Open();
            await dbConn.ExecuteAsync("UPDATE `user` SET UserName = @UserName WHERE Id = @Id", user);
            dbConn.Close();
        }

        [HttpGet]
        public async Task UpdatePartialSample()
        {
            var users = await UserRepository.Take(5).ToListAsync();
            var random = new Random(Environment.TickCount);
            foreach (var user in users)
            {
                user.Age = random.Next(1, 120);
                user.UserName = Guid.NewGuid().ToString();
                UserRepository.UpdatePartial(user, "Age");
            }
            await UnitOfWork.SaveChanges();
        }

        [HttpGet]
        public async Task UpdateBatchSample()
        {
            var users = await UserRepository.Take(5).ToListAsync();
            var random = new Random(Environment.TickCount);
            foreach (var user in users)
            {
                user.Age = random.Next(1, 120);
                user.UserName = Guid.NewGuid().ToString();
            }

            UserRepository.UpdateMany(users);
            await UnitOfWork.SaveChanges();
        }

        [HttpGet]
        public async Task AddBatchSample()
        {
            List<UserModel> userModels = new List<UserModel>();
            for (int i = 0; i < 5; i++)
            {
                UserModel userModel = new UserModel
                {
                    UserName = Guid.NewGuid().ToString(),
                    Age = new Random(Environment.TickCount).Next(1, 120),
                };
                userModels.Add(userModel);
            }

            await UserRepository.InsertMany(userModels);
            await UnitOfWork.SaveChanges();
        }

        [HttpGet]
        public async Task DeleteBatchByIdsSample()
        {
            var userIds = await UserRepository.Select(x => x.Id).Take(5).ToArrayAsync();
            UserRepository.Delete(userIds);
            await UnitOfWork.SaveChanges();
        }

        #region Transaction Scope
        [HttpGet]
        public async Task<UserModel> TransactionScopeSample()
        {
            UnitOfWork.OpenTransactionScope();
            UserModel userModel = new UserModel
            {
                UserName = Guid.NewGuid().ToString(),
                Age = new Random(Environment.TickCount).Next(1, 120),
            };
            await UserRepository.Insert(userModel);
            await UnitOfWork.SaveChanges();

            return userModel;
        }

        [HttpGet]
        public async Task<UserModel> TransactionScopeSample2()
        {
            UnitOfWork.OpenTransactionScope();
            UserModel userModel = new UserModel
            {
                UserName = Guid.NewGuid().ToString(),
                Age = new Random(Environment.TickCount).Next(1, 120),
            };

            try
            {
                await UserRepository.Insert(userModel);
                await UnitOfWork.SaveChanges();

                if (userModel.Id > 0)
                {
                    throw new InvalidOperationException("异常测试");
                }
            }
            catch (Exception)
            {
                UnitOfWork.SetTransactionScopeFailed();
                throw;
            }

            return userModel;
        }

        [HttpGet]
        public async Task<UserModel> TransactionScopeSample3()
        {
            UnitOfWork.OpenTransactionScope();
            UserModel userModel = new UserModel
            {
                UserName = Guid.NewGuid().ToString(),
                Age = new Random(Environment.TickCount).Next(1, 120),
            };

            await UnitOfWork.TransactionDecorator(async trans =>
            {
                await UserRepository.Insert(userModel);
                await UnitOfWork.SaveChanges();
            });

            await UnitOfWork.TransactionDecorator(async trans =>
            {
                await UserRepository.Insert(userModel);
                await UnitOfWork.SaveChanges();
            });

            return userModel;
        }

        [HttpGet]
        public async Task<UserModel> TransactionScopeSample4()
        {
            UserModel userModel = new UserModel
            {
                UserName = Guid.NewGuid().ToString(),
                Age = new Random(Environment.TickCount).Next(1, 120),
            };

            await UnitOfWork.TransactionDecorator(async trans =>
            {
                await UserRepository.Insert(userModel);
                await UnitOfWork.SaveChanges();
            });

            await UnitOfWork.TransactionDecorator(async trans =>
            {
                UnitOfWork.TryOpen();
                userModel.Id = await UnitOfWork.ExecuteScalarAsync<int>("INSERT INTO `user` (Id, UserName, Age) VALUES (@Id, @UserName, @Age); SELECT LAST_INSERT_ID();", userModel, trans);
            });

            return userModel;
        }

        [HttpGet]
        public async Task<UserModel> TransactionScopeSample5()
        {
            UserModel userModel = new UserModel
            {
                UserName = Guid.NewGuid().ToString(),
                Age = new Random(Environment.TickCount).Next(1, 120),
            };

            await UnitOfWork.TransactionDecorator(async trans =>
            {
                UnitOfWork.TryOpen();
                userModel.Id = await UnitOfWork.ExecuteScalarAsync<int>("INSERT INTO `user` (Id, UserName, Age) VALUES (@Id, @UserName, @Age); SELECT LAST_INSERT_ID();", userModel, trans);
            });

            await UnitOfWork.TransactionDecorator(async trans =>
            {
                await UserRepository.Insert(userModel);
                await UnitOfWork.SaveChanges();
            });

            return userModel;
        }
        #endregion
    }
}
